package com.sk.ccflower.server.lib.sysUtils;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;

import org.apache.log4j.Logger;

import com.sk.ccflower.lib.define.BaseCfg;
import com.sk.ccflower.lib.define.Consts;
import com.sk.ccflower.server.lib.sysBeans.ProcCtlBean;
import com.sk.ccflower.server.lib.sysBeans.ProcInfoBean;
import com.sun.messaging.ConnectionConfiguration;
import com.sun.messaging.ConnectionFactory;
import com.sun.messaging.Destination;
import com.sun.messaging.jms.Connection;
import com.sun.messaging.jms.JMSException;
import com.sun.messaging.jms.Message;
import com.sun.messaging.jms.Session;

/**
 * @author eunwoogi
 * @since 2009. 11. 11.
 */
public class ProcLauncherEx extends Thread {

	private Logger log;
	private String command;
	public short cflag = 1;
	public	String name;
	public Process process = null;
	private boolean once;
	private String redirectPath;
	public StringBuffer sb;
	private boolean errRead;
	private EventHandler evtHandle;
	public boolean m_bProcStart = true;
	
	public ProcLauncherEx(String command, boolean once) {
		this.log = Logger.getLogger(getClass());
		this.command = command;
		this.once = once;
		if (once) {
			this.sb = new StringBuffer();
		}
		
		if ( this.command.indexOf( "." ) >= 0 ) {
			this.name = this.command.substring( 0, this.command.indexOf(".") );
		} else {
			this.name = this.command;
		}
	}
	
	public ProcLauncherEx(String command, short cflag, boolean once) {
		this.log = Logger.getLogger(getClass());
		this.command = command;
		this.cflag = cflag;
		this.once = once;
		if (once) {
			this.sb = new StringBuffer();
		}
		
		if ( this.command.indexOf( "." ) >= 0 ) {
			this.name = this.command.substring( 0, this.command.indexOf(".") );
		} else {
			this.name = this.command;
		}
	}
	
	public void setReadErrStream() {
		this.errRead = true;
	}

	public void addEventHandler(EventHandler evtHandle) {
		this.evtHandle = evtHandle;
	}

	public void setRedirectPath(String redirectPath) {
		this.redirectPath = redirectPath;
	}
	
	public void notify2Observer( String name ) {
		ConnectionFactory myFac = null;
		Connection myConnection = null;
		Session mySession = null;
		Destination myDest = null;
		MessageProducer myProducer = null;
		
		if ( name.indexOf( "." ) >= 0 ) {
			name = name.substring( 0, name.indexOf(".") );
		}
		
		try {
			SysResCollect coll = new SysResCollect();
			
			ProcInfoBean bean = new ProcInfoBean();
			bean.pid = coll.getPid( name );
			bean.name = name;
			bean.cflag = this.cflag;
			bean.dflag = Consts.PROCESS_STATUS.DOWN_SYSTEM;
			bean.status = Consts.UP;
			
			myFac = new ConnectionFactory();
			myFac.setProperty( ConnectionConfiguration.imqAddressList, "localhost:7676, broker2:6000, broker3:9999" );
			myFac.setProperty( ConnectionConfiguration.imqReconnectEnabled, "true" );
			
			myConnection = (Connection) myFac.createConnection();
			
			mySession = (Session) myConnection.createSession( false, Session.AUTO_ACKNOWLEDGE );
			
			myDest = new com.sun.messaging.Queue( BaseCfg.IPC_MQ_NAME+"_observer" );
			
			myProducer = mySession.createProducer( myDest );
			
			ObjectMessage outMsg = mySession.createObjectMessage();
			outMsg.setObject( bean );
			
			myProducer.send( outMsg );
			
			log.debug( "ProcLauncherEx.notify2Observer() : SUCCESS Send()! -> "+bean.name );
		} catch ( Exception e ) {
			log.error( "ProcLauncherEx.notify2Observer()", e );
			notify2Observer( name );
		} finally {
			try {
				if  ( null != mySession )		mySession.close();
				if  ( null != myConnection )	myConnection.close();
			} catch ( Exception e ) {
				log.error( e );
			}
		}
	}
	
	public void notify2Observer( ProcInfoBean bean ) {
		ConnectionFactory myFac = null;
		Connection myConnection = null;
		Session mySession = null;
		Destination myDest = null;
		MessageProducer myProducer = null;
		
		try {
			myFac = new ConnectionFactory();
			myFac.setProperty( ConnectionConfiguration.imqAddressList, "localhost:7676, broker2:6000, broker3:9999" );
			myFac.setProperty( ConnectionConfiguration.imqReconnectEnabled, "true" );
			
			myConnection = (Connection) myFac.createConnection();
			
			mySession = (Session) myConnection.createSession( false, Session.AUTO_ACKNOWLEDGE );
			
			myDest = new com.sun.messaging.Queue( BaseCfg.IPC_MQ_NAME+"_observer" );
			
			myProducer = mySession.createProducer( myDest );
			
			ObjectMessage outMsg = mySession.createObjectMessage();
			outMsg.setObject( bean );
			
			myProducer.send( outMsg );
			
			log.debug( "ProcLauncherEx.notify2Observer() : SUCCESS Send()! -> name("+bean.name+") cflag("+bean.cflag+") status("+bean.status+")" );
		} catch ( Exception e ) {
			log.error( "ProcLauncherEx.notify2Observer()", e );
			notify2Observer( bean );
		} finally {
			try {
				if  ( null != mySession )		mySession.close();
				if  ( null != myConnection )	myConnection.close();
			} catch ( Exception e ) {
				log.error( e );
			}
		}
	}
	
	public Process launcher() {
		try {
			ProcessBuilder pb = new ProcessBuilder(this.command.split(" "));
			pb.redirectErrorStream(true);

			Process proc = null;

			proc = pb.start();

			this.log.info("프로세스 매니져에 의해 실행되었습니다.[" + this.command
					+ "], RedirectPath=[" + this.redirectPath + "]");
			if (this.evtHandle != null) {
				this.evtHandle.action(this.command, Consts.UP);
			}
			return proc;
		} catch (IOException ex) {
			this.log.error(ex.toString(), ex);
		} catch (Exception ex) {
			this.log.error(ex.toString(), ex);
		}
		return null;
	}

	public void sleeping(int sec) {
		try {
			Thread.sleep(sec * 1000);
		} catch (InterruptedException ex) {
		}
	}
	
	public void run() {
		InputStream is = null;
		InputStream eis = null;
		BufferedInputStream bis = null;
		BufferedInputStream ebis = null;
		ReadThreadEx t = null;
		ReadThreadEx t1 = null;
		
		ProcLauncherQueueThread thrd = new ProcLauncherQueueThread( this );
		thrd.start();
		
		while ( true ) {
			try {
				if ( false == m_bProcStart ) {
					Thread.sleep( 100 );
					continue;
				}
				
				this.process = this.launcher();
				if (this.process == null) {
					throw new Exception("프로세스 생성에 실패했습니다.");
				}
				
				is = this.process.getInputStream();
				bis = new BufferedInputStream(is);
				t = new ReadThreadEx(bis, this.redirectPath, this.sb);
				t.start();
				
				if (this.errRead && !OsUtil.isWindows()) {
					eis = this.process.getErrorStream();
					ebis = new BufferedInputStream(eis);
					t1 = new ReadThreadEx(ebis, this.redirectPath, this.sb);
					t1.start();
				}
				
				// ProcObserver에 통지(IPC : MessageQueue)
				notify2Observer( this.command );
				
				this.process.waitFor();
				
				this.evtHandle.action( this.command, Consts.DOWN );
				
				this.sleeping(3);
			} catch (InterruptedException ex) {
				this.sleeping(5);
				ex.printStackTrace();
			} catch (Exception e) {
				this.sleeping(5);
				this.log.error(e.toString(), e);
			} finally {
				try {
					if (is != null)				is.close();
					if (bis != null)			bis.close();
					if (eis != null)			eis.close();
					if (ebis != null)			ebis.close();
					if (this.process != null)	this.process.destroy();
				} catch (IOException ex1) {
					ex1.printStackTrace();
				}
				if ( this.once )	break;
				
				//this.evtHandle.action( this.command, Consts.DOWN );
				//log.debug( "ProcLauncherEx.run() : Terminate Process -> "+this.name );
			}
		}
	}
	
	public static void killProcess( String name ) throws Exception {
		String osName = System.getProperties().getProperty("os.name");
		
		if ( osName.toLowerCase().indexOf("linux") > -1 ) {
			SysResCollect coll = new SysResCollect();
			int pid = coll.getPid( name );
			if ( pid > -1 ) {
				killProcess( pid );
			}
		}
	}
	
	public static void killProcess( int pid ) throws Exception {
		String osName = System.getProperties().getProperty("os.name");
		
		if ( osName.toLowerCase().indexOf("linux") > -1 ) {
			Runtime rt = Runtime.getRuntime();
			String[] cmd = { "sh", "-c", "kill -9 "+pid };
	        Process proc = rt.exec( cmd );
	        
	        proc.waitFor();
	        proc.destroy();
		}
	}

}

class ReadThreadEx extends Thread {
	private String redirectPath;

	private File reFile;
	private OutputStream os;
	private BufferedOutputStream bos;

	private BufferedInputStream bis;
	private StringBuffer sb;

	public ReadThreadEx(BufferedInputStream bis, String redirectPath,
			StringBuffer sb) {
		this.redirectPath = redirectPath;
		this.bis = bis;
		this.sb = sb;
		if (redirectPath != null) {
			this.reFile = new File(this.redirectPath);
		}
	}

	private void redirect() throws Exception {
		if (this.redirectPath != null) {
			os = new FileOutputStream(this.reFile);
			bos = new BufferedOutputStream(os);
		}

		int len;
		byte[] b = new byte[512];
		while ((len = this.bis.read(b)) > 0) {
			if (bos != null) {
				bos.write(b, 0, len);
				bos.flush();
			} else {
				if (sb != null)
					sb.append(new String(b, 0, len));
				System.out.print(new String(b, 0, len));
			}
		}
	}

	public void run() {
		try {
			this.redirect();
		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			if (this.os != null) {
				try {
					this.os.close();
				} catch (IOException ex1) {
				}
			}
			if (this.bis != null) {
				try {
					this.bis.close();
				} catch (IOException ex2) {
				}
			}
			if (this.bos != null) {
				try {
					this.bos.close();
				} catch (IOException ex3) {
				}
			}
		}
	}

}

class ProcLauncherQueueThread extends Thread {
	
	private Logger log;
	private ConnectionFactory m_fac = null;
	private Connection m_connection = null;
	private Session m_session = null;
	private Destination m_dest = null;
	private MessageConsumer m_consumer = null;
	private ProcLauncherEx m_parent = null;
	
	public ProcLauncherQueueThread( ProcLauncherEx _parent ) {
		this.m_parent = _parent;
		this.log = Logger.getLogger( getClass() );
	}
	
	private void connectBroker() {
		try {
			m_fac = new ConnectionFactory();
			m_fac.setProperty( ConnectionConfiguration.imqAddressList, "localhost:7676, broker2:6000, broker3:9999" );
			m_fac.setProperty( ConnectionConfiguration.imqReconnectEnabled, "true" );
			
			m_connection = (Connection) m_fac.createConnection();
			
			m_session = (Session) m_connection.createSession( false, Session.AUTO_ACKNOWLEDGE );
			
			m_dest = new com.sun.messaging.Queue( BaseCfg.IPC_MQ_NAME+"_launcher"+this.m_parent.name );
			
			m_consumer = m_session.createConsumer( m_dest );
			
			m_connection.start();
		} catch ( Exception e ) {
			try {
				Thread.sleep( 1000 );
			} catch ( Exception e1 ) {
				
			}
			connectBroker();
		}
	}
	
	public void run() {
		ProcCtlBean bean = null;
		
		connectBroker();
		
		while ( true ) {
			try {
				bean = null;
				
				Message inMsg = (Message) m_consumer.receive();
				ObjectMessage objMsg = (ObjectMessage) inMsg;
				
				bean = (ProcCtlBean) objMsg.getObject();
				
				if ( null != bean ) {
					log.debug( "ProcLauncherQueueThread : SUCCESS receive() -> name("+bean.name+") cflag("+bean.cflag+")" );
					
					if ( Consts.STOP == bean.cflag )
					{
						this.m_parent.m_bProcStart = false;
						
						this.m_parent.process.destroy();
						this.m_parent.process = null;
						
						// 프로세스 상태 통지
						ProcInfoBean iBean = new ProcInfoBean();
						iBean.name = bean.name;
						iBean.pid = -1;
						iBean.status = Consts.DOWN;
						iBean.cflag = this.m_parent.cflag;
						iBean.dflag = Consts.PROCESS_STATUS.DOWN_OPERATOR;
						iBean.operator = bean.operator;
						this.m_parent.notify2Observer( iBean );
					}
					else if ( Consts.RESTART == bean.cflag )
					{
						this.m_parent.m_bProcStart = true;
						
						this.m_parent.process.destroy();
						this.m_parent.process = null;
					}
					else if ( Consts.START == bean.cflag )
					{
						this.m_parent.m_bProcStart = true;
					}
				}
				
				Thread.sleep( 10 );
			} catch ( JMSException e ) {
				try {
					if  ( null != m_session )		m_session.close();
					if  ( null != m_connection )	m_connection.close();
					
					connectBroker();
				} catch ( Exception e1 ) {
					
				}
			} catch ( Exception e ) {
				
			}
		}
	}
	
}
